package com.example.OnlineStorage.service.ShareFiles;

import com.example.OnlineStorage.Dao.FileSHA256Dao;
import com.example.OnlineStorage.Dao.ShareFilePermissionsDao;
import com.example.OnlineStorage.Dao.ShareFilesDao;
import com.example.OnlineStorage.enums.GlobalVariables_String;
import com.example.OnlineStorage.exception.CustomException;
import com.example.OnlineStorage.pojo.entity.*;
import com.example.OnlineStorage.util.*;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigInteger;
import java.util.*;

@Service
public class IShareFilesService implements ShareFilesService {

    private final ShareFilesDao shareFilesDao;
    private final ShareFilePermissionsDao shareFilePermissionsDao;

	private final FileSHA256Dao fileSha256Dao;

    public IShareFilesService(ShareFilesDao shareFilesDao,
                              ShareFilePermissionsDao shareFilePermissionsDao,
							  FileSHA256Dao fileSHA256Dao
    ) {
        this.shareFilesDao = shareFilesDao;
        this.shareFilePermissionsDao = shareFilePermissionsDao;
		this.fileSha256Dao = fileSHA256Dao;
    }

    /**
     * 创建共享空间
     *
     * @param name 共享文件夹名
     * @param UUID 用户 UUID
     * @return
     */
    @SneakyThrows
    @Override
    public String createShareFiles(String name, String UUID) {
        try {
            if (!name.matches("^[a-zA-Z0-9\\u4e00-\\u9fa5]+$")) {
                throw new RuntimeException("名称不能包含特殊字符");
            }
            User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
            ShareFiles shareFiles = new ShareFiles();
            shareFiles.setName(name);
            shareFiles.setCreateUserId(user.getId());
            shareFiles.setFileDataJson(new Directory("root", "/").toJson());
            shareFilesDao.insert(shareFiles);
            return shareFiles.toString();
        } catch (Exception e) {
            if (e.getCause() != null) {
                String Cause = e.getCause().toString();
                if (Cause.startsWith("java.sql.SQL")) {
                    throw new CustomException(500, "服务器异常.请联系管理员.");
                }
            }
            throw new CustomException(400, e.getMessage());
        }
    }



    /**
     * 在共享文件夹里面创建文件夹
     *
     * @param shareFilesId 共享文件夹ID
     * @param fromPath     在哪个路径下创建
     * @param name         创建的名称
     * @param UUID         用户 UUID
     * @return 返回该共享文件夹的所有文件信息
     */
    @SneakyThrows
    @Override
    public String createDirectory(Long shareFilesId, String fromPath, String name, String UUID) {
        try {
            if (!name.matches("^[a-zA-Z0-9\\u4e00-\\u9fa5]+$")) {
                throw new RuntimeException("名称不能包含特殊字符");
            }
	        ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
	        if (shareFiles == null) {
		        throw new RuntimeException("该共享文件夹不存在");
	        }
            User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
            ShareFilePermissions sfp = new ShareFilePermissions();
            sfp.setShareFilesId(shareFilesId);
            sfp.setUserId(user.getId());
            Long shareFilesPermissionId = shareFilePermissionsDao.select(sfp);
            if (!PermissionsControl.isUpload(shareFilesPermissionId)) {
                throw new RuntimeException("没有上传权限");
            }
            
            Node root = Node.fromJson(shareFiles.getFileDataJson());
            Node node = root.getChildByPath(fromPath);
            if (node == null || node.isFile()) {
                throw new RuntimeException("路径选择错误");
            }
            node.createChild(name, null, true, "directory", null);
            shareFiles.setFileDataJson(root.toJson());
			shareFiles.setSize(root.getNodeSize());
            shareFilesDao.update(shareFiles);
            return root.toJson();
        } catch (Exception e) {
            if (e.getCause() != null) {
                String Cause = e.getCause().toString();
                if (Cause.startsWith("java.sql.SQL")) {
                    throw new CustomException(500, "服务器异常.请联系管理员.");
                }
            }
            throw new CustomException(400, e.getMessage());
        }
    }

    @SneakyThrows
    @Override
    public Map<String, Object> isExist(Long shareFilesId, String fromPath, String name, String intactSHA256, List<String> AllChunkSHA256, String UUID) {
        try {
            User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
            ShareFilePermissions sfp = new ShareFilePermissions();
            sfp.setShareFilesId(shareFilesId);
            sfp.setUserId(user.getId());
            Long shareFilesPermissions = shareFilePermissionsDao.select(sfp);
			if(shareFilesPermissions==null){
				throw new RuntimeException("共享文件夹不存在");
			}
            if (!PermissionsControl.isUpload(shareFilesPermissions)) {
                throw new RuntimeException("没有上传权限");
            }
            Map<String, Object> map = new HashMap<>();
            if (Verify.VerifyFileIsExistCourseResource(intactSHA256)) {
                ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
                String[] Dir = name.split("/");
                name = Dir[Dir.length - 1];
                String type = IO.getFileType(GlobalVariables_String.File_Path_Files.getValue() + intactSHA256);
                BigInteger fileSize = IO.getFileSize(GlobalVariables_String.File_Path_Files.getValue() + intactSHA256);
                Node root = Node.fromJson(shareFiles.getFileDataJson());
                Node node = root.getChildByPath(fromPath);
                if (node == null || node.isFile()) {
                    throw new RuntimeException("路径选择错误");
                }
                node.createChild(name, intactSHA256, false, type, fileSize);
                shareFiles.setFileDataJson(root.toJson());
				shareFiles.setSize(root.getNodeSize());
                shareFilesDao.update(shareFiles);
                map.put("isExist", true);
                map.put("data", root.toJson());
                return map;
            }
            for (String SHA_256 : AllChunkSHA256) {
                if (!SHA_256.equals(Redis.HGET(intactSHA256, SHA_256))) {
                    map.put("isExist", false);
                    map.put("ChunkSHA256", SHA_256);
                    break;
                }
            }
            return map;
        } catch (Exception e) {
            if (e.getCause() != null) {
                String Cause = e.getCause().toString();
                if (Cause.startsWith("java.sql.SQL")) {
                    throw new CustomException(500, "服务器异常.请联系管理员.");
                }
            }
			System.out.println(e);
            throw new CustomException(400, e.getMessage());
        }
    }

    @SneakyThrows
    @Override
    public Map<String, Object> putChunk(Long shareFilesId,
                                        String fromPath,
                                        String name,
                                        String intactSHA256,
                                        String ChunkSHA256,
                                        List<String> AllChunkSHA256,
                                        MultipartFile ChunkFile,
                                        String UUID) {
        String tempPath = GlobalVariables_String.File_Path_Temp.getValue() + intactSHA256;
        try {
            User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
            ShareFilePermissions sfp = new ShareFilePermissions();
            sfp.setShareFilesId(shareFilesId);
            sfp.setUserId(user.getId());
	        Long shareFilesPermissions = shareFilePermissionsDao.select(sfp);
	        if(shareFilesPermissions==null){
		        throw new RuntimeException("共享文件夹不存在");
	        }
	        if (!PermissionsControl.isUpload(shareFilesPermissions)) {
		        throw new RuntimeException("没有上传权限");
	        }
            // 检查文件是否已存在
            boolean isExist = Verify.VerifyFileIsExistCourseResource(intactSHA256);
            Map<String, Object> map = new HashMap<>();
            if (isExist) {
                // 处理文件已存在的情况
                ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
                String[] Dir = name.split("/");
                name = Dir[Dir.length - 1];
                String type = IO.getFileType(GlobalVariables_String.File_Path_Files.getValue() + intactSHA256);
                BigInteger fileSize = IO.getFileSize(GlobalVariables_String.File_Path_Files.getValue() + intactSHA256);
                Node root = Node.fromJson(shareFiles.getFileDataJson());
                Node node = root.getChildByPath(fromPath);
                if (node == null || node.isFile()) {
                    throw new RuntimeException("路径选择错误");
                }
                node.createChild(name, intactSHA256, false, type, fileSize);
                shareFiles.setFileDataJson(root.toJson());
				shareFiles.setSize(root.getNodeSize());
                shareFilesDao.update(shareFiles);
                map.put("isExist", true);
                map.put("data", root.toJson());
                return map;
            }

            // 验证块是否存在
            if (Redis.HEXISTS(intactSHA256, ChunkSHA256)) {
                for (String SHA_256 : AllChunkSHA256) {
                    if (!Redis.HEXISTS(intactSHA256, SHA_256)) {
                        map.put("isExist", false);
                        map.put("ChunkSHA256", SHA_256);
                        return map;
                    }
                }
            }

            // 验证上传的块的SHA256
            String SHA_256 = SHA256.calculateFileSHA256(ChunkFile);
            if (!Objects.equals(SHA_256, ChunkSHA256)) {
                throw new RuntimeException("ChunkSHA256错误");
            }
			
            if (Redis.HLEN(intactSHA256) == 0) {
                IO.createFolder(tempPath);
            }
	        
	        // 写入块到临时文件夹
	        IO.writeFile(ChunkFile, tempPath + "/" + SHA_256);
	        
	        // 记录块到Redis
	        Redis.HSET(intactSHA256, ChunkSHA256, ChunkSHA256);
			
            // 检查是否所有块都已上传
            if (Redis.HLEN(intactSHA256) == AllChunkSHA256.size()) {
                String intactSHA256_ = SHA256.calculateIncrementalSHA256(AllChunkSHA256);
                if (!Objects.equals(intactSHA256, intactSHA256_)) {
                    throw new RuntimeException("intactSHA256错误");
                }

                // 合并文件块并删除临时文件夹
                String writePath = GlobalVariables_String.File_Path_Files.getValue() + intactSHA256;
                IO.deleteTempFolder(tempPath);

                
                ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
                String[] Dir = name.split("/");
                name = Dir[Dir.length - 1];
                String type = IO.getFileType(GlobalVariables_String.File_Path_Files.getValue() + intactSHA256);
                BigInteger fileSize = IO.getFileSize(GlobalVariables_String.File_Path_Files.getValue() + intactSHA256);
                Node root = Node.fromJson(shareFiles.getFileDataJson());
                Node node = root.getChildByPath(fromPath);
                if (node == null || node.isFile()) {
                    throw new RuntimeException("路径选择错误");
                }
				Long count = fileSha256Dao.addCount(intactSHA256);
				if (count != 1) {
					throw new CustomException(500,"文件上传成功,文件计数写入数据库失败,可以尝试第二次请求");
				}
                node.createChild(name, intactSHA256, false, type, fileSize);
                shareFiles.setFileDataJson(root.toJson());
				shareFiles.setSize(root.getNodeSize());
                shareFilesDao.update(shareFiles);
                map.put("isExist", true);
                map.put("data", root.toJson());
                return map;


            }

            // 如果不是所有块都已上传，则检查缺失的块
            for (String sha256 : AllChunkSHA256) {
                if (!Redis.HEXISTS(intactSHA256, sha256)) {
                    map.put("isExist", false);
                    map.put("ChunkSHA256", sha256);

                }
            }
            return map;
        } catch (Exception e) {
            if (e.getCause() != null) {
                String Cause = e.getCause().toString();
                if (Cause.startsWith("java.sql.SQL")) {
                    throw new CustomException(500, "服务器异常.请联系管理员.");
                }
            }
            if (e.getMessage().equals("文件块上传失败")) {
                Redis.HDEL(intactSHA256, ChunkSHA256);
            }
            if (e.getMessage().startsWith("intactSHA256")) {
                IO.deleteTempFolder(tempPath);
            }
            throw new CustomException(400, e.getMessage());
        }
    }

    @SneakyThrows
    @Override
    public String reviseName(Long shareFilesId,
                             String fromPath,
                             String newName,
                             String UUID) {

        try {
			
			if (!newName.matches("^[a-zA-Z0-9\\u4e00-\\u9fa5]+$")) {
				throw new RuntimeException("名称不能包含特殊字符");
			}
	        ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
	        if (shareFiles == null) {
		        throw new RuntimeException("该共享文件夹不存在");
	        }
			User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
			ShareFilePermissions sfp = new ShareFilePermissions();
			sfp.setShareFilesId(shareFilesId);
			sfp.setUserId(user.getId());
			Long shareFilesPermissionId = shareFilePermissionsDao.select(sfp);
			if (!PermissionsControl.isUpdate(shareFilesPermissionId)) {
				throw new RuntimeException("没有修改权限");
			}
			
			Node root = Node.fromJson(shareFiles.getFileDataJson());
			Node node = root.getChildByPath(fromPath);
			if (node == null || node.isFile()) {
				throw new RuntimeException("路径选择错误");
			}
	        Node parentNode = root.getChildByPath(node.getParentPath());
	        if(parentNode.getChildren().containsKey(newName)){
		        throw new RuntimeException("该目录下已存在同名文件");
	        }
			node.reviseName(newName,parentNode);
			shareFiles.setFileDataJson(root.toJson());
			shareFiles.setSize(root.getNodeSize());
			shareFilesDao.update(shareFiles);
			return root.toJson();
        } catch (Exception e) {
            if (e.getCause() != null) {
                String Cause = e.getCause().toString();
                if (Cause.startsWith("java.sql.SQL")) {
                    throw new CustomException(500, "服务器异常.请联系管理员.");
                }
            }
            throw new CustomException(400, e.getMessage());
        }
    }

	@SneakyThrows
    @Override
	public String deleteNode(Long shareFilesId,
                             String fromPath,
                             String UUID){
		try{
			ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
			if (shareFiles == null) {
				throw new RuntimeException("该共享文件夹不存在");
			}
			User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
			ShareFilePermissions sfp = new ShareFilePermissions();
			sfp.setShareFilesId(shareFilesId);
			sfp.setUserId(user.getId());
			Long shareFilesPermissionId = shareFilePermissionsDao.select(sfp);
			if (!PermissionsControl.isDelete(shareFilesPermissionId)) {
				throw new RuntimeException("没有删除权限");
			}
			
			Node root = Node.fromJson(shareFiles.getFileDataJson());
			Node node = root.getChildByPath(fromPath);
			if (node == null || node.isFile()) {
				throw new RuntimeException("路径选择错误");
			}
			Node cacheNode =  Node.fromJson(node.toJson());
			List<String> AllSha256 = new ArrayList<>();
			if(root.removeChildByPath(fromPath)){
				AllSha256 = cacheNode.getAllSha256();
				String uuid = java.util.UUID.randomUUID() +"/";
				Redis.HSET("cache"+shareFiles.getId(),uuid+cacheNode.getName(),cacheNode.toJson());
				Redis.EXPIRE("cache"+shareFiles.getId(),60 * 60 * 24 * 10);
			}
			if(!AllSha256.isEmpty()){
				fileSha256Dao.subCounts(AllSha256);
			}
			shareFiles.setFileDataJson(root.toJson());
			shareFiles.setSize(root.getNodeSize());
			shareFilesDao.update(shareFiles);
			return root.toJson();
		} catch (Exception e){
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			throw new CustomException(400, e.getMessage());
		}
	}

	@SneakyThrows
    @Override
	public Map<String, Object> deleteNodes(Long shareFilesId, List<String> fromPath, String UUID){
		try{
			ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
			if (shareFiles == null) {
				throw new RuntimeException("该共享文件夹不存在");
			}
			if (fromPath.isEmpty()) {
				throw new RuntimeException("路径选择错误");
			}
			List<String> RemoveSha256 = new ArrayList<>();
			List<String> RemovePath = new ArrayList<>();
			List<String> FailPath = new ArrayList<>();

			User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
			ShareFilePermissions sfp = new ShareFilePermissions();
			sfp.setShareFilesId(shareFilesId);
			sfp.setUserId(user.getId());
			Long shareFilesPermissionId = shareFilePermissionsDao.select(sfp);
			if (!PermissionsControl.isDelete(shareFilesPermissionId)) {
				throw new RuntimeException("没有删除权限");
			}
			Node root = Node.fromJson(shareFiles.getFileDataJson());
			for (String path : fromPath) {
				Node node = root.getChildByPath(path);
				if (node == null) {
					FailPath.add(path);
					continue;
				}
				Node cacheNode =  Node.fromJson(node.toJson());
				if(root.removeChildByPath(path)){
					RemoveSha256.addAll(cacheNode.getAllSha256());
					RemovePath.add(path);
					String uuid = java.util.UUID.randomUUID() +"/";
					Redis.HSET("cache"+shareFiles.getId(),uuid+cacheNode.getName(),cacheNode.toJson());
					Redis.EXPIRE("cache"+shareFiles.getId(),60 * 60 * 24 * 10);
				}
			}
			if(!RemoveSha256.isEmpty()){
				fileSha256Dao.subCounts(RemoveSha256);
			}
			shareFiles.setFileDataJson(root.toJson());
			shareFiles.setSize(root.getNodeSize());
			shareFilesDao.update(shareFiles);
			Map<String, Object> map = new HashMap<>();
			map.put("RemovePath", RemovePath);
			map.put("FailPath", FailPath);
			map.put("data", root.toJson());
			return map;
		} catch (Exception e){
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			throw new CustomException(400, e.getMessage());
		}
	}

	@SneakyThrows
    @Override
	public String recoveryNode(Long shareFilesId, String fromPath, String cacheName, String UUID){
		try{
			ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
			if (shareFiles == null) {
				throw new RuntimeException("该共享文件夹不存在");
			}
			User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
			ShareFilePermissions sfp = new ShareFilePermissions();
			sfp.setShareFilesId(shareFilesId);
			sfp.setUserId(user.getId());
			Long shareFilesPermissionId = shareFilePermissionsDao.select(sfp);
			if (!PermissionsControl.isUpload(shareFilesPermissionId)) {
				throw new RuntimeException("没有修改权限");
			}
			Node root = Node.fromJson(shareFiles.getFileDataJson());
			Node node = root.getChildByPath(fromPath);
			if (node == null || node.isFile()) {
				throw new RuntimeException("路径选择错误");
			}
			String cacheNodeJson = Redis.HGET("cache"+shareFiles.getId(),cacheName);
			if (cacheNodeJson == null) {
				throw new RuntimeException("缓存不存在");
			}
			Node cacheNode = Node.fromJson(cacheNodeJson);
			node.copy(cacheNode);
			if(!cacheNode.getAllSha256().isEmpty()){
				fileSha256Dao.addCounts(cacheNode.getAllSha256());
			}
			shareFiles.setFileDataJson(root.toJson());
			shareFiles.setSize(root.getNodeSize());
			shareFilesDao.update(shareFiles);
			Redis.HDEL("cache"+shareFiles.getId(),cacheName);
			return root.toJson();
		} catch (Exception e){
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			throw new CustomException(400, e.getMessage());
		}
	}

	@SneakyThrows
    @Override
	public String moveNode(Long shareFilesId, String fromPath, String toPath, String UUID){
		try{
			if(fromPath.equals(toPath)){
				throw new RuntimeException("移动的路径跟，目标路径不能一样.");
			}
			User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
			ShareFilePermissions sfp = new ShareFilePermissions();
			sfp.setShareFilesId(shareFilesId);
			sfp.setUserId(user.getId());
			Long shareFilesPermissionId = shareFilePermissionsDao.select(sfp);
			if (shareFilesPermissionId == null) {
				throw new RuntimeException("共享文件夹不存在.");
			}
			if (!PermissionsControl.isUpdate(shareFilesPermissionId)) {
				throw new RuntimeException("没有修改权限");
			}
			ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
			Node root = Node.fromJson(shareFiles.getFileDataJson());
			Node node = root.getChildByPath(fromPath);
			if (node == null) {
				throw new RuntimeException("路径选择错误");
			}
            Node cacheNode =  Node.fromJson(node.toJson());
            root.removeChildByPath(fromPath);
            Node toNode = root.getChildByPath(toPath);
            if (toNode == null|| toNode.isFile()) {
                throw new RuntimeException("路径选择错误,移动到的路径只能是文件夹路径");
            }
            toNode.copy(cacheNode);
            shareFiles.setFileDataJson(root.toJson());
			shareFiles.setSize(root.getNodeSize());
            shareFilesDao.update(shareFiles);
			return root.toJson();
		} catch (Exception e){
			if (e.getCause() != null) {
				String Cause = e.getCause().toString();
				if (Cause.startsWith("java.sql.SQL")) {
					throw new CustomException(500, "服务器异常.请联系管理员.");
				}
			}
			System.out.println(e);
			throw new CustomException(400, e.getMessage());
		}
	}

    @SneakyThrows
    @Override
    public Set<Object> getCacheList(Long shareFilesId, String UUID){
        try{
            User user = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));
            ShareFilePermissions sfp = new ShareFilePermissions();
            sfp.setShareFilesId(shareFilesId);
            sfp.setUserId(user.getId());
            ShareFiles shareFiles = shareFilesDao.select(shareFilesId);
            if (shareFiles == null) {
                throw new RuntimeException("该共享文件夹不存在");
            }
            return Redis.HKEYS("cache"+shareFiles.getId());
        } catch (Exception e){
            if (e.getCause() != null) {
                String Cause = e.getCause().toString();
                if (Cause.startsWith("java.sql.SQL")) {
                    throw new CustomException(500, "服务器异常.请联系管理员.");
                }
            }
            throw new CustomException(400, e.getMessage());
        }
    }


    @SneakyThrows
    @Override
    public void addShareFile(Long userId, Long shareFileId, Long permission, String UUID) {
        try{
            User nowUser = User.fromJson(Redis.HGET(Redis.GET(UUID), "user"));

            if (Objects.equals(nowUser.getId(), userId)) {
                throw new CustomException(402, "不可以添加自己创建的共享空间");
            }

            ShareFilePermissions shareFilePermissions = new ShareFilePermissions();
            shareFilePermissions.setUserId(nowUser.getId());
            shareFilePermissions.setShareFilesId(shareFileId);
            shareFilePermissions.setPermissions(permission);

            // TODO 需要判断要添加的共享文件夹是否分享

            shareFilePermissionsDao.insert(shareFilePermissions);

        } catch (Exception e){
            if (e.getCause() != null) {
                String Cause = e.getCause().toString();
                if (Cause.startsWith("java.sql.SQL")) {
                    throw new CustomException(500, "服务器异常.请联系管理员.");
                }
            }
            throw new CustomException(400, e.getMessage());
        }

    }

    @SneakyThrows
    public void createShareLink(Long fromPath, String UUID){

    }
}
